﻿using System.Collections.Generic;
using System.Linq;
using CodeParser.Tokens;

namespace CodeParser.Expressions
{
    public abstract class ExpressionParser
    {

        public abstract Expression Parse(Token[] tokens, int index);

        public Expression ParseSubExpression(Token[] tokens, int index, int? maxLength, IEnumerable<ExpressionParser> parsers)
        {
            if (parsers == null) parsers = new List<ExpressionParser>(); // make empty list
            var parserArray = parsers.ToArray();
            var sequence = new Sequence();
            TokenChain unsupported = null;
            var maxIndex = maxLength.HasValue ? index + maxLength - 1 : tokens.Length - 1;
            while (index <= maxIndex)
            {
                Expression expression = null;
                for (var parserIndex = 0; parserIndex < parserArray.Length; parserIndex++)
                {
                    expression = parserArray[parserIndex].Parse(tokens, index);
                    if (expression != null)
                    {
                        if (unsupported != null)
                        {
                            if (unsupported.Tokens.Count == 1)
                            {
                                sequence.Add(new SingleToken() { Token = unsupported.Tokens[0], TokenLength = 1 });
                            }
                            else
                            {
                                sequence.Add(unsupported);
                                unsupported.StringLength = unsupported.Tokens[unsupported.Tokens.Count - 1].Index +
                                                           unsupported.Tokens[unsupported.Tokens.Count - 1].Value.Length - unsupported.StringIndex;
                            }
                            sequence.TokenLength += unsupported.TokenLength;
                            unsupported = null;
                        }
                        sequence.Add(expression);
                        sequence.TokenLength += expression.TokenLength;
                        index += expression.TokenLength;
                        break;
                    }
                }
                if (expression == null)
                {
                    if (unsupported == null) unsupported = new TokenChain() { StringIndex = tokens[index].Index};
                    unsupported.Tokens.Add(tokens[index]);
                    unsupported.TokenLength++;
                    index++;
                }
            }
            if (unsupported != null)
            {
                if (unsupported.Tokens.Count == 1)
                {
                    sequence.Add(new SingleToken() {Token = unsupported.Tokens[0]});
                }
                else
                {
                    sequence.Add(unsupported);
                    unsupported.StringLength = unsupported.Tokens[unsupported.Tokens.Count - 1].Index +
                                                           unsupported.Tokens[unsupported.Tokens.Count - 1].Value.Length;
                }
                sequence.TokenLength += unsupported.TokenLength;
            }

            switch (sequence.Expressions.Count)
            {
                case 0:
                    return null;
                case 1:
                    return sequence.Expressions[0];
                default:
                    sequence.StringIndex = sequence.Expressions[0].StringIndex;
                    var last = sequence.Expressions[sequence.Expressions.Count - 1];
                    sequence.StringLength = last.StringIndex + last.StringLength - sequence.StringIndex;
                    return sequence;
            }
        }

    }
}
